package ncme.auto.framework.core.services.Impl.base;

import ncme.auto.framework.core.entities.ResMsg;
import ncme.auto.framework.core.entities.service.MapContainer;
import ncme.auto.framework.core.entities.service.SelectResult;
import ncme.auto.framework.core.entities.service.TreeNode;
import ncme.auto.framework.core.exceptions.AutoException;
import ncme.auto.framework.core.exceptions.ExceptionEnum;
import ncme.auto.framework.core.utils.BeanUtil;
import ncme.auto.framework.core.utils.ServiceUtil;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public abstract class BaseAutoEntityServiceImpl<E> extends DatabaseServiceImpl<E> {

    //region######################################## auto ###########################################

    public ResMsg baseQuerySearch(Map<String, Object> searchMap) {
        SelectResult<E> entitiesByMap = getEntitiesByMap(searchMap, true, false);
        return ResMsg.successRes(entitiesByMap.toMap());
    }

    public ResMsg baseQueryAddObj(Object addEntity) {
        return baseQueryAdd((E) addEntity);
    }

    public ResMsg baseQueryAdd(E addEntity) {
        Integer newId = addEntity(addEntity);
        Map<String, Object> map = new HashMap<>();
        map.put("newId", newId);
        return ResMsg.successRes(map);
    }

    public ResMsg baseQueryEditObj(Object updateEntity) {
        return baseQueryEdit((E) updateEntity);
    }

    public ResMsg baseQueryEdit(E updateEntity) {
        editEntity(updateEntity);
        return ResMsg.successRes(null);
    }

    public ResMsg baseQueryDelObj(Object delEntity) {
        return baseQueryDelete((E) delEntity);
    }

    public ResMsg baseQueryDelete(E delEntity) {
        deleteEntity(delEntity);
        return ResMsg.successRes(null);
    }

    public ResMsg baseQueryDelOneById(Integer deleteId) {
        return baseQueryDelete(getEntityById(deleteId, false));
    }

    //endregion

    //region######################################## get ###########################################

    /**
     * 获取总数
     */
    public Integer getEntitiesCount() {
        return selectCount();
    }

    /**
     * 通过条件Map获取实体list
     */
    public SelectResult<E> getEntitiesByMap(Map<String, Object> searchMap, boolean isLike, boolean reverse) {
        Integer[] pageInfo = getPageInfoFromMap(searchMap);
        return select(searchMap, pageInfo[0], pageInfo[1], isLike, reverse);
    }

    @Deprecated
    public List<E> getEntitiesFromOneField(String field, Object value) {
        Map<String, Object> searchMap = new HashMap<>();
        searchMap.put(field, value);
        return getEntitiesByMap(searchMap, false, false).getPageData();
    }

    @Deprecated
    public E getEntityByMap(Map<String, Object> searchMap, boolean allowNull) {
        SelectResult<E> entitiesByMap = getEntitiesByMap(searchMap, false, false);
        if (entitiesByMap.getTotalNum() == 0 && allowNull) return null;
        else if (entitiesByMap.getTotalNum() == 0 && !allowNull)
            throw new AutoException(ExceptionEnum.NOT_EXIST, "查询结果不存在" + ENTITY_NAME + searchMap);
        else if (entitiesByMap.getTotalNum() == 1) return entitiesByMap.getPageData().get(0);
        else throw new AutoException(ExceptionEnum.REPETITION, "查询结果不唯一");
    }

    public E getEntityFromOneField(String field, Object value, boolean allowNull) {
        return getEntityByMap(new MapContainer<String, Object>().put(field, value).getMap(), allowNull);
    }

    public E getEntityById(Integer id, boolean allowNull) {
        Map<String, Object> id1 = new MapContainer<String, Object>().put("id", id).getMap();
        return getEntityByMap(id1, allowNull);
    }

    public E getEntityFromParam(Object param, boolean byId, boolean allowNull) {
        Field[] paramFields = param.getClass().getDeclaredFields();
        //优先找id
        if (byId) {
            for (Field paramField : paramFields) {
                int id = 0;
                if (paramField.getName().equals("id")) try {
                    Object idField = getFieldValue(param, "id");
                    if (idField == null) break;
                    id = Integer.parseInt(idField.toString());
                } catch (Exception e) {
                    throw new AutoException(ExceptionEnum.PARAM_FIELD, "参数取值错误@Integer.parseInt");
                }
                if (paramField.getName().equals("id")) return getEntityById(id, allowNull);
            }
        }
        //其次找唯一字段
        for (String fieldName : VERIFY_REPETITION_FIELD) {
            for (Field paramField : paramFields) {
                if (paramField.getName().equals(fieldName)) {
                    Object fieldValue = getFieldValue(param, fieldName);
                    if (fieldValue == null) break;
                    E entity = getEntityFromOneField(fieldName, fieldValue.toString(), allowNull);
                    if (entity != null) return entity;
                }
            }
        }
        //最后找组合唯一字段
        if (VERIFY_MULTI_REPETITION_FIELD.size() > 0) {
            List<Object> fieldsValues = new ArrayList<>();
            for (String fieldName : VERIFY_MULTI_REPETITION_FIELD) {
                fieldsValues.add(getFieldValue(param, fieldName));
            }
            String md5 = ServiceUtil.getMd5(fieldsValues);
            return getEntityFromOneField("md5", md5, allowNull);
        }
        //不是id、唯一字段或组合唯一字段则
        if (allowNull) return null;
        else throw new AutoException(ExceptionEnum.NOT_EXIST);
    }

    /**
     * 将业务实体的 List 转换成树状结构，需要实体类中包含 id 及 parentId 字段
     */
    public List<TreeNode> getEntityTree(List<E> entities, Integer rootId) {
        class TreeUtil {
            protected List<TreeNode> getChildNodes(Map<Integer, List<E>> parentIdKeyMaps, Integer parentId) {
                try {
                    List<E> ts = parentIdKeyMaps.get(parentId);
                    if (ts == null) return null;
                    List<TreeNode> childNodes = new ArrayList<>();
                    for (E t : ts) {
                        Integer id = Integer.parseInt(BeanUtil.getFieldValue(t, "id").toString());
                        //构造node
                        TreeNode treeNode = new TreeNode();
                        treeNode.setEntity(t);
                        treeNode.setChildNodes(getChildNodes(parentIdKeyMaps, id));
                        //添加node
                        childNodes.add(treeNode);
                    }
                    return childNodes;
                } catch (Exception e) {
                    throw new AutoException("getChildNodes异常", e);
                }
            }
        }
        Map<Integer, List<E>> parentIdKeyMaps = new HashMap<>();
        try {
            for (E entity : entities) {
                Object value = BeanUtil.getFieldValue(entity, "parentId");
                if (value == null) continue;
                int parentId = Integer.parseInt(value.toString());
                if (!parentIdKeyMaps.containsKey(parentId))
                    parentIdKeyMaps.put(parentId, new ArrayList<>());
                parentIdKeyMaps.get(parentId).add(entity);
            }
            return new TreeUtil().getChildNodes(parentIdKeyMaps, rootId);
        } catch (Exception e) {
            throw new AutoException("getChildNodes异常");
        }
    }

    //endregion

    //region######################################## add ###########################################

    public Integer addEntity(E addEntity) {
        //对实体对象验重
        if (verifyEntityRepetition(addEntity) != null) throw new AutoException(ExceptionEnum.JDBC, "有重复项");
        return insert(addEntity);
    }

    public List<Integer> addEntityBatch(List<E> addEntities) {
        List<Integer> newIds = new ArrayList<>();
        for (E addEntity : addEntities) {
            newIds.add(addEntity(addEntity));
        }
        return newIds;
    }

    //endregion

    //region######################################## edit ###########################################

    public void editEntity(E editEntity) {
        Integer entityId = Integer.parseInt(getFieldValue(editEntity, "id").toString());
        E oldEntity = getEntityById(entityId, false);
        //对更新后的实体对象验重
        if (verifyEntityRepetition(editEntity, entityId) != null) throw new AutoException(ExceptionEnum.JDBC, "有重复项");
        //验重通过，更新数据库
        update(editEntity, oldEntity);
    }

    //endregion

    //region######################################## delete ###########################################

    public void deleteEntity(E delEntity) {
        //检验是否存在
        delEntity = getEntityFromParam(delEntity, true, false);
        deleteEntityMapper((Integer) getFieldValue(delEntity, "id"));
        delete(delEntity);
    }

    public void deleteEntityBatch(List<E> delEntities) {
        for (E addEntity : delEntities) {
            deleteEntity(addEntity);
        }
    }

    private void deleteEntityMapper(Integer id) {
        try {
            Map<String, ?> beansOfType = applicationContext.getBeansOfType(BaseAutoMapperServiceImpl.class);
            Set<String> strings = beansOfType.keySet();
            for (String string : strings) {
                String mapperName = string.replace("MapperServiceImpl", "");
                BaseAutoMapperServiceImpl<?, ?, ?> autoMapperService = (BaseAutoMapperServiceImpl<?, ?, ?>) applicationContext.getBean(string);
                //primary
                if (mapperName.startsWith(ENTITY_NAME_LOWER)) {
                    //校验该实体确实是mapperService中的primary，如果是的话，secondary必然也是对应的~吧
                    if (autoMapperService.getPrimary().equals(ENTITY_NAME_LOWER + "Id")) {
                        //删除primaryId下的所有mapper
                        autoMapperService.deleteMappers(id, autoMapperService.getIdsFromId(id, true), true);
                    }
                } else if (mapperName.endsWith(ENTITY_NAME_LOWER)) {
                    //校验该实体确实是mapperService中的primary，如果是的话，secondary必然也是对应的~吧
                    if (autoMapperService.getSecondary().equals(ENTITY_NAME_LOWER + "Id")) {
                        //删除secondaryId下的所有mapper
                        autoMapperService.deleteMappers(id, autoMapperService.getIdsFromId(id, false), false);
                    }
                }
            }
        } catch (Exception e) {
            if (!(e instanceof AutoException && ExceptionEnum.getExceptionEnum(((AutoException) e).getCode()) == ExceptionEnum.NOT_EXIST))
                throw new AutoException(ExceptionEnum.JDBC, "实体映射删除失败：" + ENTITY_NAME_LOWER + "Id:" + id + " " + e.getMessage());
        }
    }

    //endregion

    //region######################################## tools ###########################################

    private Integer[] getPageInfoFromMap(Map<String, Object> map) {
        try {
            int page = 0, size = Integer.MAX_VALUE;
            if (map != null && map.containsKey("page") && map.containsKey("size")) {
                page = Integer.parseInt(map.get("page").toString());
                size = Integer.parseInt(map.get("size").toString());
                if (page < 0 || size < 1) throw new AutoException(ExceptionEnum.PARAM_FIELD, "page或size值错误");
            }
            return new Integer[]{page, size};
        } catch (Exception e) {
            throw new AutoException(ExceptionEnum.PARAM_FIELD, "分页参数提取异常");
        }
    }

    /**
     * 用于编辑时的验重，与新增验重的区别是：当查找到相同验重字段的实体，还需判断是不是自身，不是自身才算重名
     *
     * @param paramEntity-需要验重的参数实体
     * @param oldId-需要修改的实体         id
     */
    private E verifyEntityRepetition(E paramEntity, Integer oldId) {
        E oneEntityFromParam = getEntityFromParam(paramEntity, false, true);
        if (oneEntityFromParam != null && !getFieldValue(oneEntityFromParam, "id").equals(oldId))
            return oneEntityFromParam;
        else return null;
    }

    /**
     * 用于新增时的验重
     *
     * @param paramEntity-需要验重的参数实体
     */
    private E verifyEntityRepetition(E paramEntity) {
        return verifyEntityRepetition(paramEntity, null);
    }

    //endregion

    //region######################################## 兼容old ###########################################

    @Deprecated
    public E getEntityFromId(Integer entityId, boolean allowNull) {
        return getEntityById(entityId, allowNull);
    }

    @Deprecated
    public E getEntityFromId(Integer entityId) {
        return getEntityById(entityId, true);
    }

    @Deprecated
    public E getEntityFromRedisProp(String propName, String value, boolean allowNull) {
        return getEntityFromOneField(propName, value, allowNull);
    }

    @Deprecated
    public E getEntityFromRedisProp(String propName, String value) {
        return getEntityFromOneField(propName, value, true);
    }

    @Deprecated
    public E getEntityByMultiVerify(List<Object> verifyFields) {
        return getEntityFromOneField("md5", ServiceUtil.getMd5(verifyFields), false);
    }

    @Deprecated
    public E getEntityFromParam(Object param, boolean byId) {
        return getEntityFromParam(param, byId, true);
    }

    @Deprecated
    public Integer getAllEntityNum() {
        return getEntitiesCount();
    }

    @Deprecated
    public List<E> getAllEntities() {
        return getEntitiesByMap(new HashMap<>(), true, false).getPageData();
    }

    @Deprecated
    public List<E> getEntitiesByPropName(String propName, Object value, boolean isLike) {
        return getEntitiesByMap(new MapContainer<String, Object>().put(propName, value).getMap(), isLike, false).getPageData();
    }

    @Deprecated
    public List<E> getEntitiesByPropName(String propName, Object value) {
        return getEntitiesByMap(new MapContainer<String, Object>().put(propName, value).getMap(), true, false).getPageData();
    }

    @Deprecated
    public List<E> getEntitiesByPage(Integer page, Integer size) {
        return getEntitiesByMap(new MapContainer<String, Object>().put("page", page).put("size", size).getMap(), false, false).getPageData();
    }

    @Deprecated
    public List<E> getEntitiesByMap(Map<String, Object> searchMap, boolean reverse) {
        return getEntitiesByMap(searchMap, false, reverse).getPageData();
    }

    @Deprecated
    public List<E> getEntitiesByOneField(String field, Object value) {
        return getEntitiesFromOneField(field, value);
    }

    //endregion

}
